package com.xunyin.modules.login.rest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xunyin.api.ApiResult;
import com.xunyin.api.XunyinException;
import com.xunyin.enums.AppIdKeyEnum;
import com.xunyin.enums.ConfigShortEnum;
import com.xunyin.enums.ErrorEnum;
import com.xunyin.modules.log.AppLog;
import com.xunyin.modules.login.param.LoginParam;
import com.xunyin.modules.user.domain.SysXbAppUser;
import com.xunyin.modules.user.domain.SysXbuser;
import com.xunyin.modules.user.service.SysXbAppUserService;
import com.xunyin.modules.user.service.SysXbuserService;
import com.xunyin.modules.user.service.mapper.SysXbuserMapper;
import com.xunyin.utils.CommonConst;
import com.xunyin.utils.CommonUtil;
import com.xunyin.utils.HttpClientUtil;
import com.xunyin.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 小程序端登录控制器
 * </p>
 *
 * @author admin
 * @since 2022-10-18
 */
@Slf4j
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Api(value = "小程序端登录模块", tags = "共享空间:小程序端登录模块")
public class LoginController {
    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    private final SysXbuserService sysXbuserService;

    private final SysXbAppUserService sysXbAppUserService;

    private final SysXbuserMapper sysXbuserMapper;

    private String decryptResult = "";

    /**
     * 普通用户-小程序登录入口
     *
     * @param loginParam
     * @return
     */
    @AppLog(value = "获取微信消息订阅模板与运营者的手机号", type = 1)
    //@AuthCheck
    @PostMapping("/getMinApp")
    @ApiOperation(value = "普通用户-小程序登录入口", notes = "普通用户-小程序登录入口")
    public JSONObject getMinApp(@Validated @RequestBody LoginParam loginParam, HttpServletResponse response) throws IOException {
        log.info("getMinApp -> param={}", loginParam);
        String appId = loginParam.getAppId();
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
        url = url.replace("APPID", appId);
        url = url.replace("SECRET", AppIdKeyEnum.getAppKeyByAppId(appId));
        url = url.replace("JSCODE", loginParam.getCode());
        log.info("url={}", url);
        String result = HttpClientUtil.doGet(url);
        log.info("result={}", result);
        Map map = JSON.parseObject(result);
        String openid = map.get("openid") + "";
        String unionId = map.get("unionid") + "";
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("openid", openid);
        jsonObject.put("unionId", unionId);
/*        SysXbAppUser stAppUser = new SysXbAppUser();
        stAppUser.setAppId(openid);
        stAppUser.setStatus(CommonConst.ENABLE_FLAG);*/
        List<SysXbAppUser> appUsers = sysXbAppUserService.selectSysXbAppUserList(null, openid);
        if (appUsers.size() > 0 && appUsers.size() < 2) {
            SysXbAppUser stAppUser1 = appUsers.get(0);
            //判断用户是否存在，如果存在，选择一个登陆，一个手机号可能有多个用户，账号就是手机号,不存在，跳转失败页面
            SysXbuser axPkuser = sysXbuserService.getLoginUser(stAppUser1.getTelephone());
            if (axPkuser != null) {
                jsonObject.put("userId", axPkuser.getId());
            }
        }

        return CommonUtil.successJson(jsonObject);
    }

    /**
     * 获取微信消息订阅模板与运营者的手机号
     *
     * @param loginParam
     * @return
     */
    @AppLog(value = "获取微信消息订阅模板与运营者的手机号", type = 1)
    //@AuthCheck
    @PostMapping("/getCfg")
    @ApiOperation(value = "获取微信消息订阅模板与运营者的手机号", notes = "获取微信消息订阅模板与运营者的手机号")
    public ApiResult<Map<String, Object>> getCfg(@Validated @RequestBody LoginParam loginParam) throws IOException {
        log.info("getCfg -> param={}", loginParam);
        String appId = loginParam.getAppId();
        // 返回 resultMap
        Map<String, Object> resultMap = new HashMap<String, Object>();
        //
/*        if("wxbc07428913aad7e6".equals(appId)) {
            resultMap.put("serverPhone", "19996813121");
            resultMap.put("tmplIds", "S-WJah9EK2097P2Gk-GGH3sEi5iu4Ls48kQFdCEcViE");
        }*/

        //
        if ("wx6345a04c42ae10e3".equals(appId)) {
            resultMap.put("serverPhone", "19835690983");
            resultMap.put("tmplIds", "xsD87o3kwfWSN_vMjEezLoOVy8q4HHhP9QNXmgwUSQ8");
        }

        return ApiResult.ok(resultMap).setMsg("登陆成功");
    }

    /**
     * 首页-获取用户信息
     *
     * @param loginParam
     * @return
     */
    @AppLog(value = "首页-获取用户信息", type = 1)
    //@AuthCheck
    @PostMapping("/getUser")
    @ApiOperation(value = "首页-获取用户信息", notes = "首页-获取用户信息")
    public JSONObject getUser(@Validated @RequestBody LoginParam loginParam) throws IOException {
        JSONObject jsonObject = new JSONObject();
        SysXbAppUser stAppUser = new SysXbAppUser();
        String openId = loginParam.getOpenId();
        logger.info("首页-获取用户信息, openid={}", openId);
        if ("".equals(openId)) {
            jsonObject = CommonUtil.errorJson(ErrorEnum.E_20011);
        } else {
            stAppUser.setAppId(openId);
            stAppUser.setStatus(CommonConst.ENABLE_FLAG);
            List<SysXbAppUser> appUsers = sysXbAppUserService.selectSysXbAppUserList(null, openId);
            //List<StAppUser> appUsers = loginService.selectRecordList(stAppUser);
            if (appUsers.size() > 0 && appUsers.size() < 2) {
                SysXbAppUser stAppUser1 = appUsers.get(0);
                //判断用户是否存在，如果存在，选择一个登陆，一个手机号可能有多个用户，账号就是手机号,不存在，跳转失败页面
                //AxPkuser axPkuser = loginService.getLoginUser(stAppUser1.getTelephone());
                SysXbuser axPkuser = sysXbuserService.getLoginUser(stAppUser1.getTelephone());
                if (axPkuser != null) {
                    axPkuser.setIsUsed(CommonConst.ENABLE_FLAG);
                    axPkuser.setLastLoginTime(new Date());
                    /*loginService.updateLoginUser(axPkuser);*/
                    LambdaQueryWrapper<SysXbuser> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(SysXbuser::getId, axPkuser.getId());
                    sysXbuserMapper.update(axPkuser, wrapper);
                    jsonObject = CommonUtil.successJson(axPkuser);
                }
            } else {
                jsonObject = CommonUtil.successJson();
            }
        }
        return jsonObject;
    }

    /**
     * 根据用户Id获取用户类型
     *
     * @param loginParam
     * @return
     */
    @AppLog(value = "根据用户Id获取用户类型", type = 1)
    //@AuthCheck
    @PostMapping("/getUserType")
    @ApiOperation(value = "根据用户Id获取用户类型", notes = "根据用户Id获取用户类型")
    public ApiResult<Map<String, Object>> getUserType(@Validated @RequestBody LoginParam loginParam) {
        SysXbuser sysXbuser = sysXbuserService.getById(loginParam.getUserId());
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("teahouseId", sysXbuser.getTeahouseId());
        resultMap.put("userType", sysXbuser.getUserType());
        resultMap.put("id", sysXbuser.getId());
        resultMap.put("userAccount", sysXbuser.getUserAccount());
        return ApiResult.ok(resultMap).setMsg("获取成功");
    }

    /**
     * 普通用户-小程序登录授权
     *
     * @param loginParam
     * @return
     */
    @AppLog(value = "普通用户-小程序登录授权", type = 1)
    //@AuthCheck
    @PostMapping("/getPhoneNumber")
    @ApiOperation(value = "普通用户-小程序登录授权", notes = "普通用户-小程序登录授权")
    public ApiResult<Map<String, Object>> getPhoneNumber(@Validated @RequestBody LoginParam loginParam) {
        logger.info("普通用户-小程序登录授权, loginParam={}", JSONObject.toJSONString(loginParam));
        if (StringUtils.isEmpty(loginParam.getCode())) {
            throw new XunyinException(ErrorEnum.E_90003.getErrorMsg());
        }
        String appId = loginParam.getAppId();
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
        url = url.replace("APPID", appId);
        url = url.replace("SECRET", AppIdKeyEnum.getAppKeyByAppId(appId));
        url = url.replace("JSCODE", loginParam.getCode());
        String result = HttpClientUtil.doGet(url);
        logger.info("普通用户-小程序登录授权, result={}", result);
        Map map = JSON.parseObject(result);
        String session_key = map.get("session_key") + "";
        String openid = map.get("openid") + "";
        String unionId = map.get("unionid") + "";
/*        byte[] encData = Base64.getDecoder().decode(loginParam.getEncryptedData());
        byte[] ivData = Base64.getDecoder().decode(loginParam.getIv());
        byte[] sessionKey = Base64.getDecoder().decode(session_key);*/
        String phoneNumber = "";
        Short appType = ConfigShortEnum.SOURCE_TYPE_MINAPP.getCode();

        // 返回 resultMap
        Map<String, Object> resultMap = new HashMap<String, Object>();
        try {

            String s = wxDecrypt(loginParam.getEncryptedData(), session_key, loginParam.getIv());
            Map<String, Object> phoneMap = JSON.parseObject(s, Map.class);
            phoneNumber = phoneMap.get("phoneNumber") + "";
            //JSONObject object = JSONObject.parseObject(s);
            //object.getString("phoneNumber");
            //Object number = object.get("phoneNumber");

            //decrypt1(session_key, loginParam.getIv(), loginParam.getEncryptedData());
            //String decryptResult = decrypt1(session_key, loginParam.getIv(), loginParam.getEncryptedData());

            //byte[] phoneArray = decrypt(sessionKey, ivData, encData);
            //String temp =  decryptResult;
            //String encoded =new String(phoneArray, "UTF-8");
            //Map<String, Object> phoneMap = JSON.parseObject(new String(phoneArray), Map.class);
            //Map<String, Object> phoneMap = JSON.parseObject(decryptResult, Map.class);
            //phoneNumber = phoneMap.get("phoneNumber") + "";
            SysXbuser sysXbuser = sysXbuserService.getLoginUser(phoneNumber);
            SysXbuser axPkuser = new SysXbuser();
            if (sysXbuser == null) {
                // 保存用户
/*              axPkuser.setUserAccount(phoneNumber);
                axPkuser.setUserName(phoneNumber);
                axPkuser.setIsUsed(CommonConst.ENABLE_FLAG);
                axPkuser.setLastLoginTime(new Date());
                axPkuser.setCreateTime(new Date());
                axPkuser.setStatus(CommonConst.ENABLE_FLAG);
                axPkuser.setUserType(1);
                axPkuser.setUserStatus(1);*/
                Integer id = sysXbuserService.saveLoginUser(phoneNumber, openid);
                //resultMap.put("teahouseId", sysXbuser.getTeahouseId());
                resultMap.put("userType", 1);
                resultMap.put("task", 2);
                resultMap.put("openId", openid);
                resultMap.put("id", id);
                resultMap.put("userAccount", phoneNumber);
            } else {
                resultMap.put("teahouseId", sysXbuser.getTeahouseId());
                resultMap.put("userType", sysXbuser.getUserType());
                resultMap.put("task", 2);
                resultMap.put("openId", openid);
                resultMap.put("id", sysXbuser.getId());
                resultMap.put("userAccount", sysXbuser.getUserAccount());
            }

            // 如果小程序普通（消费者）用户不存在，则新建一个
            List<SysXbAppUser> appUsers = sysXbAppUserService.selectSysXbAppUserList(phoneNumber, openid);
            if (appUsers.size() == 0) {
                SysXbAppUser sysXbAppUser = new SysXbAppUser();
                //保存登录信息
                sysXbAppUser.setTelephone(phoneNumber);
                sysXbAppUser.setAppId(openid);
                sysXbAppUser.setStatus(CommonConst.ENABLE_FLAG);
                sysXbAppUser.setAppId(openid);
                sysXbAppUser.setAppType(appType);
                sysXbAppUser.setCreateTime(new Date());
                sysXbAppUser.setUnionId(unionId);
                sysXbAppUser.setWxAppId(appId);
                sysXbAppUserService.save(sysXbAppUser);
            }
        } catch (Exception e) {
            //e.printStackTrace();
            throw new XunyinException("小程序登录保存用户信息报错");
        }
        return ApiResult.ok(resultMap).setMsg("登陆成功");
    }

    static {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String wxDecrypt(String encrypted, String sessionKey, String iv) throws Exception {
        byte[] encrypData = Base64.decodeBase64(encrypted);
        byte[] ivData = Base64.decodeBase64(iv);
        byte[] sKey = Base64.decodeBase64(sessionKey);
        String decrypt = decrypt(sKey, ivData, encrypData);
        return decrypt;
    }

    public static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception {
//        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
//        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
//        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        //解析解密后的字符串
        String resultString = null;
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            resultString = new String(cipher.doFinal(encData), "UTF-8");
        } catch (Exception e) {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            resultString = new String(cipher.doFinal(encData), "UTF-8");
        }
        return resultString;
    }


    public String decrypt1(String sessionKey, String ivData, String encrypData) throws Exception {
        byte[] encData = Base64.decodeBase64(encrypData);
        byte[] iv = Base64.decodeBase64(ivData);
        byte[] key = Base64.decodeBase64(sessionKey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        //解析解密后的字符串
        String decryptResult = new String(cipher.doFinal(encData), "UTF-8");
        //Map<String, Object> phoneMap = JSON.parseObject(decryptResult, Map.class);
        //return String.valueOf(cipher.doFinal(encData));
        return new String(cipher.doFinal(encData), "UTF-8");
    }

    public synchronized static byte[] decryptbase(byte[] key, byte[] iv, byte[] encData)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
            InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        return cipher.doFinal(encData);
    }
}
